/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/java-pdf-library-support/
 *
 * (C) Copyright 1997-2013, IDRsolutions and Contributors.
 *
 * 	This file is part of JPedal
 *
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * FormStream.java
 * ---------------
 */
package org.jpedal.objects.raw;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import org.jpedal.external.ExternalHandlers;
import org.jpedal.fonts.PdfFont;
import org.jpedal.io.ObjectStore;
import org.jpedal.io.PdfObjectReader;
import org.jpedal.parser.PdfStreamDecoder;
import org.jpedal.parser.ValueTypes;
import org.jpedal.render.T3Display;
import org.jpedal.render.T3Renderer;
import org.jpedal.utils.LogWriter;
import org.w3c.dom.Node;

/**
 * can object and creat images, set values in Appearances
 */
public class FormStream {

	final public static boolean debugUnimplemented = false;// to show unimplemented parts*/
	final public static boolean debug = false;// print info to screen

	// only display once
	private static boolean showFontMessage = false;

	/**
	 * exit when an unimplemented feature or error has occured in form/annot code
	 */
	final public static boolean exitOnError = false;

	/** handle of file reader for form streams */
	protected PdfObjectReader currentPdfFile;

	/** if true makes the javascript define on each method call, if false it sets up all JS at creation point */
	public static boolean marksNewJavascriptCode = false;

	/**
	 * stop anyone creating empty instance
	 */
	public FormStream() {}

	/**
	 * used if we want to recreate (ie for better print quality)
	 */
	public FormStream(PdfObjectReader inCurrentPdfFile) {

		this.currentPdfFile = inCurrentPdfFile;
	}

	public static final int[] id = { PdfDictionary.A, PdfDictionary.C2, PdfDictionary.Bl, PdfDictionary.E, PdfDictionary.X, PdfDictionary.D,
			PdfDictionary.U, PdfDictionary.Fo, PdfDictionary.PO, PdfDictionary.PC, PdfDictionary.PV, PdfDictionary.PI, PdfDictionary.O,
			PdfDictionary.C1, PdfDictionary.K, PdfDictionary.F, PdfDictionary.V, PdfDictionary.C2, PdfDictionary.DC, PdfDictionary.WS,
			PdfDictionary.DS, PdfDictionary.WP, PdfDictionary.DP };

	/**
	 * takes in a FormObject already populated with values for the child to overwrite
	 */
	public FormObject createAppearanceString(FormObject formObj, PdfObjectReader inCurrentPdfFile) {

		this.currentPdfFile = inCurrentPdfFile;

		init(formObj);

		return formObj;
	}

	private void init(FormObject formObject) {

		final boolean debug = false;// formObject.getPDFRef().equals("68 0 R");

		if (debug) System.out.println("------------------------------setValues-------------------------------" + formObject + ' '
				+ formObject.getObjectRefAsString());

		// set Ff flags
		int Ff = formObject.getInt(PdfDictionary.Ff);
		if (Ff != PdfDictionary.Unknown) formObject.commandFf(Ff);

		// set Javascript
		if (!marksNewJavascriptCode) // lock out to test
		resolveAdditionalAction(formObject);

		if (debug) System.out.println("AP=" + formObject.getDictionary(PdfDictionary.AP));

		// at the moment only handles static
		// (and not dynamic which are created at Runtime if
		// formObject.getBoolean(PdfDictionary.NeedAppearances) is true
		setupAPimages(formObject, debug);

		// set H
		int key = formObject.getNameAsConstant(PdfDictionary.H);
		if (key != PdfDictionary.Unknown) {
			/**
			 * highlighting mode done when the mouse is pressed or held down inside the fields active area N nothing I invert the contents O invert
			 * the border P display down appearance stream, or if non available offset the normal to look down T same as P
			 * 
			 * this overides the down appearance Default value = I
			 */
			if (key == PdfDictionary.T || key == PdfDictionary.P) {
				if (!formObject.hasDownImage()) formObject.setOffsetDownApp();

			}
			else
				if (key == PdfDictionary.N) {
					// do nothing on press
					formObject.setNoDownIcon();

				}
				else
					if (key == PdfDictionary.I) {
						// invert the contents colors
						formObject.setInvertForDownIcon();

					}
		}

		// set Fonts

		String textStream = formObject.getTextStreamValue(PdfDictionary.DA);

		if (textStream != null) {
			decodeFontCommandObj(textStream, formObject);

			// System.out.println("textStream="+textStream);

		}

		// code from FormStream not called but may be needed
		// this is font stream
		/*
		 * stream //turn into byte array and add // (<text>)tj String textString = formObject.getContents(); if (textString != null) { byte[]
		 * textbytes = StringUtils.toBytes(textString); System.out.println("textString="+textString); int streamLength = stream.length; byte[]
		 * newbytes = new byte[streamLength + textbytes.length]; for (int i = 0; i < newbytes.length; i++) { if (i < streamLength) newbytes[i] =
		 * stream[i]; else newbytes[i] = textbytes[i - streamLength]; } //then send into stream decoder PdfStreamDecoder textDecoder = new
		 * PdfStreamDecoder(); textDecoder.decodeStreamIntoObjects(newbytes); StringBuilder textData = textDecoder.getlastTextStreamDecoded(); if
		 * (textData != null) formObject.setTextValue(textData.toString()); } /*
		 */
	}

	/** decodes and stores the ap images */
	public void setupAPimages(FormObject formObject, boolean debug) {

		// if(formObject.getObjectRefAsString().equals("54 0 R"))
		// System.out.println("aa");

		// setup images - trickle throuh maps
		int[] values = new int[] { PdfDictionary.N, PdfDictionary.R, PdfDictionary.D }; // N must be first
		final String[] names = new String[] { "PdfDictionary.N", "PdfDictionary.R", "PdfDictionary.D" }; // N must be first

		int APcount = values.length;
		PdfObject APobj;
		BufferedImage img;
		for (int ii = 0; ii < APcount; ii++) {

			// debug=formObject.getPDFRef().equals("68 0 R") && values[ii]==PdfDictionary.N;

			APobj = formObject.getDictionary(PdfDictionary.AP).getDictionary(values[ii]);
			if (APobj != null) {

				if (debug && values[ii] == PdfDictionary.N) System.out.println(" AP (" + names[ii] + ")=" + APobj + ' '
						+ APobj.getObjectRefAsString() + " AP=" + formObject.getDictionary(PdfDictionary.AP) + " form=" + formObject + ' '
						+ formObject.getObjectRefAsString() + " stream=" + APobj.getDecodedStream() + " Off="
						+ APobj.getDictionary(PdfDictionary.Off));

				// main
				if (APobj.getDecodedStream() != null) {
					img = rotate(decode(this.currentPdfFile, APobj, formObject.getParameterConstant(PdfDictionary.Subtype)), formObject
							.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R));

					if (debug) System.out.println(img);

					if (img == null) continue;

					if (values[ii] == PdfDictionary.D) {

						formObject.setAppearanceImage(img, PdfDictionary.D, PdfDictionary.Off);

						if (debug) {
							System.out.println("D " + img);
						}

					}
					else
						if (values[ii] == PdfDictionary.N && !formObject.hasNormalOff()) {

							formObject.setAppearanceImage(img, PdfDictionary.N, PdfDictionary.Off);
							if (debug) System.out.println("N " + img);

						}
						else
							if (values[ii] == PdfDictionary.R) {
								formObject.setAppearanceImage(img, PdfDictionary.R, PdfDictionary.Off);
								if (debug) System.out.println("R " + img);
							}

				}
				else { // Off, /On Other

					// On
					PdfObject OnObj = APobj.getDictionary(PdfDictionary.On);
					if (OnObj != null) {

						img = rotate(decode(this.currentPdfFile, OnObj, formObject.getParameterConstant(PdfDictionary.Subtype)), formObject
								.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R));

						if (debug) // && values[ii]==PdfDictionary.N)
						System.out.println("On=" + OnObj + ' ' + img);

						if (img == null) continue;

						if (values[ii] == PdfDictionary.D) {

							formObject.setAppearanceImage(img, PdfDictionary.D, PdfDictionary.On);

							if (!formObject.hasDownOff()) formObject.setAppearanceImage(null, PdfDictionary.D, PdfDictionary.Off);

						}
						else
							if (values[ii] == PdfDictionary.N) {
								formObject.setAppearanceImage(img, PdfDictionary.N, PdfDictionary.On);
								formObject.setNormalOnState("On");

							}
							else
								if (values[ii] == PdfDictionary.R) {

									formObject.setAppearanceImage(img, PdfDictionary.R, PdfDictionary.On);
									if (!formObject.hasRolloverOff()) formObject.setAppearanceImage(null, PdfDictionary.R, PdfDictionary.Off);
								}
					}

					// Off
					PdfObject OffObj = APobj.getDictionary(PdfDictionary.Off);
					if (OffObj != null) {

						img = rotate(decode(this.currentPdfFile, OffObj, formObject.getParameterConstant(PdfDictionary.Subtype)), formObject
								.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R));

						if (debug) // && values[ii]==PdfDictionary.N)
						System.out.println("Off=" + OffObj + ' ' + OffObj.getDecodedStream() + ' ' + img);

						if (img == null) continue;

						if (values[ii] == PdfDictionary.D) {
							formObject.setAppearanceImage(img, PdfDictionary.D, PdfDictionary.Off);

						}
						else
							if (values[ii] == PdfDictionary.N) {
								formObject.setAppearanceImage(img, PdfDictionary.N, PdfDictionary.Off);

							}
							else
								if (values[ii] == PdfDictionary.R) {
									formObject.setAppearanceImage(img, PdfDictionary.R, PdfDictionary.Off);
								}
					}

					// Other
					Map otherValues = APobj.getOtherDictionaries();
					if (otherValues != null && !otherValues.isEmpty()) {

						if (debug) System.out.println(formObject.getObjectRefAsString() + " AP Other=" + otherValues);

						Iterator keys = otherValues.keySet().iterator();
						Object val;
						String key;
						while (keys.hasNext()) {
							key = (String) keys.next();
							val = otherValues.get(key);

							PdfObject otherObj = ((PdfObject) val);

							img = rotate(decode(this.currentPdfFile, otherObj, formObject.getParameterConstant(PdfDictionary.Subtype)), formObject
									.getDictionary(PdfDictionary.MK).getInt(PdfDictionary.R));

							if (debug) System.out.println("(other) " + key + ' ' + otherObj + ' ' + img);

							if (img == null) continue;

							if (values[ii] == PdfDictionary.D) {

								if (debug) System.out.println("D(other) set=" + img + ' ' + formObject);

								formObject.setAppearanceImage(img, PdfDictionary.D, PdfDictionary.On);

								if (!formObject.hasDownOff()) formObject.setAppearanceImage(null, PdfDictionary.D, PdfDictionary.Off);

							}
							else
								if (values[ii] == PdfDictionary.N) {

									formObject.setAppearanceImage(img, PdfDictionary.N, PdfDictionary.On);
									formObject.setNormalOnState(key);

									if (debug) System.out.println("Set N (other) " + formObject);

									if (!formObject.hasNormalOff()) formObject.setAppearanceImage(null, PdfDictionary.N, PdfDictionary.Off);

								}
								else
									if (values[ii] == PdfDictionary.R) {

										formObject.setAppearanceImage(img, PdfDictionary.R, PdfDictionary.On);

										if (!formObject.hasRolloverOff()) {
											formObject.setAppearanceImage(null, PdfDictionary.R, PdfDictionary.Off);
										}
									}
						}
					}
				}
			}
		}
	}

	/**
	 * defines actions to be executed on events 'Trigger Events'
	 * 
	 * @Action This is where the raw data is parsed and put into the FormObject
	 */
	private void resolveAdditionalAction(FormObject formObject) {

		/**
		 * entries NP, PP, FP, LP never used A action when pressed in active area ?some others should now be ignored? E action when cursor enters
		 * active area X action when cursor exits active area D action when cursor button pressed inside active area U action when cursor button
		 * released inside active area Fo action on input focus BI action when input focus lost PO action when page containing is opened, actions O of
		 * pages AA dic, and OpenAction in document catalog should be done first PC action when page is closed, action C from pages AA dic follows
		 * this PV action on viewing containing page PI action when no longer visible in viewer K action on - [javascript] keystroke in textfield or
		 * combobox modifys the list box selection (can access the keystroke for validity and reject or modify) F the display formatting of the field
		 * (e.g 2 decimal places) [javascript] V action when fields value is changed [javascript] C action when another field changes (recalculate
		 * this field) [javascript]
		 */
		int idValue;

		for (int anId : id) {

			// store most actions in lookup table to make code shorter/faster
			idValue = anId;

		}
	}

	public static BufferedImage decode(PdfObjectReader currentPdfFile, PdfObject XObject, int subtype) {
		return decode(currentPdfFile, XObject, subtype, 0, 0, 0, 1);
	}

	/**
	 * decode appearance stream and convert into VectorRenderObject we can redraw if width and height are 0 we define the size hear offsetImage - 0=
	 * no change, 1= offset, 2= invert image pScaling used by HTML - set to 1 otherwise
	 */
	public static BufferedImage decode(PdfObjectReader currentPdfFile, PdfObject XObject, int subtype, int width, int height, int offsetImage,
			float pageScaling) {
		// NOTE iconRotation kept to keep api but iconRotation is never used and NOT needed

		boolean useHires = true;

		currentPdfFile.checkResolved(XObject);
		try {

			/**
			 * generate local object to decode the stream
			 */
			ObjectStore localStore = new ObjectStore(null);

			/**
			 * create renderer object
			 */
			T3Renderer glyphDisplay = new T3Display(0, false, 20, localStore);

			// fix for hires
			if (!useHires) glyphDisplay.setOptimisedRotation(false);
			else
			// if(useHires)
			glyphDisplay.setHiResImageForDisplayMode(useHires);

			PdfStreamDecoder glyphDecoder = new PdfStreamDecoder(currentPdfFile, useHires, null); // switch to hires as well
			glyphDecoder.setParameters(false, true, 15, 0);

			glyphDecoder.setIntValue(ValueTypes.StreamType, ValueTypes.FORM);

			glyphDecoder.setBooleanValue(ValueTypes.XFormFlattening, true);
			glyphDecoder.setObjectValue(ValueTypes.ObjectStore, localStore);

			glyphDecoder.setObjectValue(ValueTypes.DynamicVectorRenderer, glyphDisplay);

			/** read any resources */
			try {
				PdfObject Resources = XObject.getDictionary(PdfDictionary.Resources);
				if (Resources != null) glyphDecoder.readResources(Resources, false);

			}
			catch (Exception e) {
				System.out.println("Exception " + e + " reading resources in XForm");
				// tell user and log
				if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
			}

			/** decode the stream */
			byte[] commands = XObject.getDecodedStream();

			if (commands != null) glyphDecoder.decodeStreamIntoObjects(commands, false);

			boolean ignoreColors = glyphDecoder.ignoreColors;

			localStore.flush();

			org.jpedal.fonts.glyph.T3Glyph form = new org.jpedal.fonts.glyph.T3Glyph(glyphDisplay, 0, 0, ignoreColors, "");

			float[] BBox = XObject.getFloatArray(PdfDictionary.BBox);

			float scaling;

			float rectX1 = 0, rectY1 = 0;
			if (BBox != null) {

				for (int ii = 0; ii < 4; ii++)
					BBox[ii] = BBox[ii] * pageScaling;

				rectX1 = (BBox[0]);
				rectY1 = (BBox[1]);
				int boxWidth = (int) (((BBox[2] + 0.5f) - BBox[0]));
				if (boxWidth < 0) boxWidth = -boxWidth;

				int boxHeight = (int) (((BBox[3] + 0.5f) - BBox[1]));
				if (boxHeight < 0) boxHeight = -boxHeight;

				if (boxWidth == 0 && boxHeight > 0) boxWidth = 1;
				if (boxWidth > 0 && boxHeight == 0) boxHeight = 1;

				// if the width and height scaling are miles apart then the width and height
				// are probably the wrong way round so swap them. and recalc the scalings.
				float ws = ((float) width) / ((float) boxWidth);
				float hs = ((float) height) / ((float) boxHeight);

				// check if dimensions are correct and alter if not
				float diff = ws - hs;
				int diffInt = (int) diff;
				if (diffInt != 0) {
					// NOTE width and height sent in need to be rotated
					// as they are not as the image is drawn
					int tmpI = width;
					width = height;
					height = tmpI;

					ws = ((float) width) / ((float) boxWidth);
					hs = ((float) height) / ((float) boxHeight);
				}
				// NOTE now we re set the width and height to scaled
				// value of Bounding box to keep the orientation

				// if scaling less than 1 use 1
				if (ws < 1 || hs < 1) {
					scaling = 1;
					width = boxWidth;
					height = boxHeight;
				}
				else {
					// use larger scaling as will produce better image
					if (ws > hs) {
						scaling = ws;
						height = (int) (boxHeight * scaling);
					}
					else {
						scaling = hs;
						width = (int) (boxWidth * scaling);
					}

					// make sure image position is scaled
					rectX1 *= scaling;
					rectY1 *= scaling;
				}

			}
			else {

				float defaultSize = 20;
				if (height < defaultSize) height = (int) defaultSize;
				if (width < defaultSize) width = (int) defaultSize;

				float ws = (width) / defaultSize;
				float hs = (height) / defaultSize;
				if (ws > hs) {
					scaling = ws;
					height = (int) (defaultSize * scaling);
				}
				else {
					scaling = hs;
					width = (int) (defaultSize * scaling);
				}

				// make sure image position is scaled
				rectX1 *= scaling;
				rectY1 *= scaling;
			}

			if (width == 0 || height == 0) return null;

			if (scaling > 1) {
				height += 2;
				width += 2;
			}

			// if offset
			if (offsetImage == 1) {
				width += 2;
				height += 2;
			}

			BufferedImage aa;
			Graphics2D g2;

			float a, b, c, d, e, f;
			int offset = height;

			float[] matrix = XObject.getFloatArray(PdfDictionary.Matrix);

			if (matrix != null) {

				a = matrix[0];
				b = matrix[1];
				c = matrix[2];
				d = matrix[3];
				// scale so they offset correctly
				e = matrix[4] * scaling * pageScaling;
				f = matrix[5] * scaling * pageScaling;

				if (c != 0) {
					aa = new BufferedImage(height, width, BufferedImage.TYPE_INT_ARGB);
					offset = width;
				}
				else {
					aa = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
					if (b < 0) {
						// already set at this
						// if(e!=0f)
						// e = matrix[4];
						// if(f!=0f)
						// f = matrix[5];
					}
					else {
						// rectX1 and rectY1 already have the scaling applied
						if (e != 0f) e = -rectX1;
						if (f != 0f) f = -rectY1;
					}

				}

				g2 = (Graphics2D) aa.getGraphics();
				AffineTransform flip = new AffineTransform();
				flip.translate(0, offset);
				flip.scale(1, -1);
				g2.setTransform(flip);

				if (debug) System.out.println(" rectX1 = " + rectX1 + " rectY1 = " + rectY1 + " width = " + width + " height = " + height);

				AffineTransform affineTransform = new AffineTransform(a, b, c, d, e, f);
				// System.out.println("BBox="+ConvertToString.convertArrayToString(BBox));
				// System.out.println("matrix="+ConvertToString.convertArrayToString(matrix));
				// System.out.println("affine="+affineTransform);
				g2.transform(affineTransform);
			}
			else {
				aa = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

				g2 = (Graphics2D) aa.getGraphics();

				AffineTransform flip = new AffineTransform();
				flip.translate(0, offset);
				flip.scale(1, -1);
				g2.setTransform(flip);
			}

			if (offsetImage == 2) // invert
			g2.scale(-1, -1);
			else
				if (offsetImage == 1) // offset
				g2.translate(1, 1);

			// add transparency for highlights
			if (subtype == PdfDictionary.Highlight) g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));

			// carry the sclaing through to the render method
			form.render(0, g2, scaling * pageScaling, true);

			g2.dispose();

			return aa;

		}
		catch (Exception e) {
			// tell user and log
			if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
			return null;
		}
		catch (Error e) {
			// tell user and log
			if (LogWriter.isOutput()) LogWriter.writeLog("Error: " + e.getMessage());

			if (ExternalHandlers.throwMissingCIDError && e.getMessage().contains("kochi")) throw e;

			return null;
		}
	}

	/**
	 * method to rotate an image through a given angle
	 * 
	 * @param src
	 *            the source image
	 * @param rotation
	 *            the angle to rotate the image through
	 * @return the rotated image
	 */
	public static BufferedImage rotate(BufferedImage src, int rotation) {
		BufferedImage dst;

		if (src == null) return null;

		// if angle is 0 we dont need to do anything
		if (rotation == 0) return src;

		try {
			double angle = rotation * Math.PI / 180;

			int w = src.getWidth();
			int h = src.getHeight();
			int newW = (int) (Math.round(h * Math.abs(Math.sin(angle)) + w * Math.abs(Math.cos(angle))));
			int newH = (int) (Math.round(h * Math.abs(Math.cos(angle)) + w * Math.abs(Math.sin(angle))));
			AffineTransform at = AffineTransform.getTranslateInstance((newW - w) / 2, (newH - h) / 2);
			at.rotate(angle, w / 2, h / 2);

			dst = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB);
			Graphics2D g2 = dst.createGraphics();
			g2.drawRenderedImage(src, at);

			g2.dispose();
		}
		catch (Error e) {
			dst = null;
		}
		return dst;
	}

	/**/
	/**
	 * join the dots and save the image for the inklist annot
	 * 
	 * NO exmaple found i switchover so moved into here for if we need
	 * 
	 * private static void commandInkList(Object field, FormObject formObject) { //the ink list join the dots array if(debug)
	 * System.out.println("inklist array="+field);
	 * 
	 * //current shape object being drawn note we pass in handle on pageLines PdfShape currentDrawShape=new PdfShape(); Rectangle rect =
	 * formObject.getBoundingRectangle();
	 * 
	 * String paths = (String)field; StringTokenizer tok = new StringTokenizer(paths,"[] ",true); int countArrays=0; boolean isFirstPoint = false;
	 * String next,first=null,second=null; while(tok.hasMoreTokens()){ next = tok.nextToken(); if(next.equals("[")){ countArrays++; isFirstPoint =
	 * true; continue; }else if(next.equals("]")){ countArrays--; continue; }else if(next.equals(" ")){ continue; }else { if(first!=null){ second =
	 * next; }else { first = next; continue; } }
	 * 
	 * if(isFirstPoint){ currentDrawShape.moveTo(Float.parseFloat(first)-rect.x,Float.parseFloat(second)-rect.y); isFirstPoint = false; }else{
	 * currentDrawShape.lineTo(Float.parseFloat(first)-rect.x,Float.parseFloat(second)-rect.y); }
	 * 
	 * first = null; } // close for s command // currentDrawShape.closeShape();
	 * 
	 * org.jpedal.objects.GraphicsState currentGraphicsState= new org.jpedal.objects.GraphicsState();
	 * 
	 * Shape currentShape = currentDrawShape.generateShapeFromPath(null, currentGraphicsState.CTM,
	 * false,null,false,null,currentGraphicsState.getLineWidth(),0);
	 * 
	 * Stroke inkStroke = currentGraphicsState.getStroke();
	 * 
	 * BufferedImage image = new BufferedImage(rect.width,rect.height,BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = (Graphics2D)image.getGraphics();
	 * g2.setStroke(inkStroke); g2.setColor(Color.red); g2.scale(1,-1); g2.translate(0,-image.getHeight()); g2.draw(currentShape);
	 * 
	 * g2.dispose();
	 * 
	 * formObject.setAppearanceImage(image,PdfDictionary.N,PdfDictionary.Off); }/
	 **/

	// ////code from DecodeCommand in AnnotStream
	/**
	 * { //the ink list join the dots array
	 * 
	 * //current shape object being drawn note we pass in handle on pageLines PdfShape currentDrawShape=new PdfShape(); Rectangle rect =
	 * formObject.getBoundingRectangle();
	 * 
	 * String paths = Strip.removeArrayDeleminators((String)field); StringTokenizer tok = new StringTokenizer(paths,"[] ",true);
	 * 
	 * boolean isFirstPoint = false; String next,first=null,second=null; while(tok.hasMoreTokens()){ next = tok.nextToken(); if(next.equals("[")){
	 * isFirstPoint = true; continue; }else if(next.equals("]")){ continue; }else if(next.equals(" ")){ continue; }else { if(first!=null){ second =
	 * next; }else { first = next; continue; } }
	 * 
	 * if(isFirstPoint){ currentDrawShape.moveTo(Float.parseFloat(first)-rect.x,Float.parseFloat(second)-rect.y); isFirstPoint = false; }else{
	 * currentDrawShape.lineTo(Float.parseFloat(first)-rect.x,Float.parseFloat(second)-rect.y); }
	 * 
	 * first = null; } // close for s command // currentDrawShape.closeShape();
	 * 
	 * org.jpedal.objects.GraphicsState currentGraphicsState=formObject.getGraphicsState();
	 * 
	 * Shape currentShape = currentDrawShape.generateShapeFromPath(null, currentGraphicsState.CTM,
	 * false,null,false,null,currentGraphicsState.getLineWidth(),0);
	 * 
	 * Stroke inkStroke = currentGraphicsState.getStroke();
	 * 
	 * BufferedImage image = new BufferedImage(rect.width,rect.height,BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = (Graphics2D)image.getGraphics();
	 * g2.setStroke(inkStroke); g2.setColor(Color.red); g2.scale(1,-1); g2.translate(0,-image.getHeight()); g2.draw(currentShape);
	 * 
	 * g2.dispose();
	 * 
	 * //ShowGUIMessage.showGUIMessage("path draw",image,"path drawn");
	 * 
	 * formObject.setNormalAppOff(image,null);
	 * 
	 * // }else if(command.equals("RD")){ // //rectangle differences left top right bottom order as recieved // //the bounds of the internal object,
	 * in from the Rect // // StringTokenizer tok = new StringTokenizer(Strip.removeArrayDeleminators((String)field)); // float left =
	 * Float.parseFloat(tok.nextToken()); // float top = Float.parseFloat(tok.nextToken()); // float right = Float.parseFloat(tok.nextToken()); //
	 * float bottom = Float.parseFloat(tok.nextToken()); // // formObject.setInternalBounds(left,top,right,bottom); // }else { // notFound = true; } /
	 **/
	public boolean hasXFADataSet() {
		return false;
	}

	/**
	 * takes the PDF commands and creates a font
	 */
	public static void decodeFontCommandObj(String fontStream, FormObject formObject) {

		// now parse the stream into a sequence of tokens
		StringTokenizer tokens = new StringTokenizer(fontStream, "() []");
		int tokenCount = tokens.countTokens();
		String[] tokenValues = new String[tokenCount];
		int i = 0;
		while (tokens.hasMoreTokens()) {
			tokenValues[i] = tokens.nextToken();
			i++;
		}

		// now work out what it does and build up info
		for (i = tokenCount - 1; i > -1; i--) {
			// System.out.println(tokenValues[i]+" "+i);

			// look for commands
			if (tokenValues[i].equals("g")) { // set color (takes 1 values
				i--;
				float col = 0;
				try {
					col = Float.parseFloat(handleComma(tokenValues[i]));
				}
				catch (Exception e) {
					LogWriter.writeLog("Error in generating g value " + tokenValues[i]);
				}

				formObject.setTextColor(new float[] { col });

			}
			else
				if (tokenValues[i].equals("Tf")) { // set font (takes 2 values - size and font
					i--;
					int textSize = 8;
					try {
						textSize = (int) Float.parseFloat(handleComma(tokenValues[i]));
						// if(textSize==0)
						// textSize = 0;//TODO check for 0 sizes CHANGE size to best fit on 0
					}
					catch (Exception e) {
						LogWriter.writeLog("Error in generating Tf size " + tokenValues[i]);
					}

					i--;// decriment for font name
					String font = null;
					try {
						font = tokenValues[i];
						if (font.startsWith("/")) font = font.substring(1);
					}
					catch (Exception e) {
						LogWriter.writeLog("Error in generating Tf font " + tokenValues[i]);
					}

					PdfFont currentFont = new PdfFont();

					formObject.setTextFont(currentFont.setFont(font, textSize));

					formObject.setTextSize(textSize);

				}
				else
					if (tokenValues[i].equals("rg") || tokenValues[i].equals("r")) {
						i--;
						float b = Float.parseFloat(handleComma(tokenValues[i]));
						i--;
						float g = Float.parseFloat(handleComma(tokenValues[i]));
						i--;
						float r = Float.parseFloat(handleComma(tokenValues[i]));

						formObject.setTextColor(new float[] { r, g, b });

						// if(debug)
						// System.out.println("rgb="+r+","+g+","+b+" rg     CHECK ="+currentAt.textColor);

					}
					else
						if (tokenValues[i].equals("Sig")) {
							LogWriter.writeFormLog("Sig-  UNIMPLEMENTED=" + fontStream + "< " + i, debugUnimplemented);
						}
						else
							if (tokenValues[i].equals("\\n")) {
								// ignore \n
								if (debug) System.out.println("ignore \\n");
							}
							else {
								if (!showFontMessage) {
									showFontMessage = true;
									LogWriter.writeFormLog("{stream} Unknown FONT command " + tokenValues[i] + ' ' + i + " string=" + fontStream,
											debugUnimplemented);

								}
							}
		}
	}

	private static String handleComma(String tokenValue) {

		// if comma used as full stop remove
		int comma = tokenValue.indexOf(',');
		if (comma != -1) tokenValue = tokenValue.substring(0, comma);

		return tokenValue;
	}

	public Node getXFA(int xfaTemplate) {
		throw new RuntimeException("getXFA Should never be called in base class");
	}
}
